/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "hilog/log.h"
#include "json_error.h"
#include "yyjson_util.h"
#include "securec.h"
#include "json_node.h"

constexpr size_t YYJSON_READ_INC_VALUE = 102400; // 100KB

static napi_ref g_ref = nullptr;

using namespace json_error;
using namespace yyjson_util;

JsonNode::JsonNode()
{
    doc_ = nullptr;
    root_ = nullptr;
    array_buffer_data_ = nullptr;
    array_buffer_length_ = 0;
}
JsonNode::~JsonNode() {}

void JsonNode::Destructor(napi_env env, void *nativeObject, [[maybe_unused]] void *finalize_hint)
{
    OH_LOG_DEBUG(LOG_APP, "JsonNode, destructor called");

    JsonNode *obj = reinterpret_cast<JsonNode *>(nativeObject);
    if (obj->array_buffer_data_ != nullptr) {
        delete[] obj->array_buffer_data_;
        obj->array_buffer_data_ = nullptr;
        obj->array_buffer_length_ = 0;
    }
    if (obj->doc_ != nullptr) {
        yyjson_doc_free(obj->doc_);
        obj->doc_ = nullptr;
    }
    delete obj;
    obj = nullptr;
}

void JsonNode::InitObjJsonValue(napi_env env, napi_value *args, JsonNode *obj)
{
    size_t byte_length = 0;
    napi_valuetype type = napi_undefined;
    if (napi_typeof(env, args[0], &type) != napi_ok) {
        JsonThrowHandle(env, json_error::NO_YYJSON_ERROR_PARAM_INVALID, "Init object json value, napi type of failed");
        return;
    }

    if (type == napi_string) {
        OH_LOG_DEBUG(LOG_APP, "Init object json value,type is string");
        napi_get_value_string_utf8(env, args[0], nullptr, 0, &byte_length);
        obj->array_buffer_data_ = new char[byte_length + YYJSON_PADDING_SIZE]();
        if (obj->array_buffer_data_ == nullptr) {
            JsonThrowHandle(env, json_error::NO_YYJSON_ERROR_MEMORY_FAILED, "Init object json value, malloc failed");
            return;
        }
        napi_get_value_string_utf8(env, args[0], obj->array_buffer_data_, byte_length + YYJSON_PADDING_SIZE,
                                   &byte_length);
    } else {
        bool isArrayBuffer = false;
        napi_is_arraybuffer(env, args[0], &isArrayBuffer);
        if (!isArrayBuffer) {
            OH_LOG_WARN(LOG_APP, "Init object json value, json data str is not array buffer");
            JsonThrowHandle(env, json_error::NO_YYJSON_ERROR_PARAM_INVALID, "Argument must be an ArrayBuffer");
            return;
        }
        OH_LOG_DEBUG(LOG_APP, "Type is not arrayBuffer");

        void *data = nullptr;
        napi_status status = napi_get_arraybuffer_info(env, args[0], &data, &byte_length);
        if (status != napi_ok) {
            OH_LOG_WARN(LOG_APP, "Napi get arraybuffer info failed.");
            JsonThrowHandle(env, json_error::NO_YYJSON_ERROR_PARAM_INVALID, "Napi get array buffer info failed");
            return;
        }
        obj->array_buffer_data_ = new char[byte_length + YYJSON_PADDING_SIZE]();
        if (obj->array_buffer_data_ == nullptr) {
            JsonThrowHandle(env, json_error::NO_YYJSON_ERROR_MEMORY_FAILED, "Init object Json Value, malloc failed");
            return;
        }
        if (memcpy_s(obj->array_buffer_data_, byte_length + YYJSON_PADDING_SIZE, data, byte_length) != EOK) {
            HandleBoundsCheckError(env, json_error::MEMCPY_S_ERROR, "memcpy_s failed while copying arraybuffer data.");
            return;
        }
    }
    obj->array_buffer_length_ = byte_length;
}

napi_value JsonNode::New(napi_env env, napi_callback_info info)
{
    OH_LOG_DEBUG(LOG_APP, "JsonNode, create object called");

    size_t argc = 1;
    napi_value args[1];
    napi_value jsThis;

    JsonNode *obj = new JsonNode();
    if (obj == nullptr) {
        JsonThrowHandle(env, json_error::NO_YYJSON_ERROR_MEMORY_FAILED, "Create object, malloc failed");
        return nullptr;
    }
    napi_get_cb_info(env, info, &argc, args, &jsThis, nullptr);
    if (argc >= 1) {
        InitObjJsonValue(env, args, obj);
    }

    // 通过napi_wrap_sendable将ArkTS对象jsThis与C++对象obj绑定
    napi_status status = napi_wrap_sendable(env, jsThis, reinterpret_cast<void *>(obj), JsonNode::Destructor, nullptr);
    if (status != napi_ok) {
        OH_LOG_WARN(LOG_APP,
                    "Failed to bind native object to js object"
                    ", return code: %{public}d",
                    status);
        delete obj;
        return jsThis;
    }

    OH_LOG_DEBUG(LOG_APP, "JsonNode, return new object");

    return jsThis;
}

napi_value JsonNode::DecodeFromArrayBuffer(napi_env env, napi_callback_info info)
{
    OH_LOG_DEBUG(LOG_APP, "Decode json str start.");

    napi_value jsThis;
    napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, nullptr);

    JsonNode *obj;
    napi_unwrap_sendable(env, jsThis, reinterpret_cast<void **>(&obj));
    if ((obj->array_buffer_data_ == nullptr) || (obj->array_buffer_length_ == 0)) {
        OH_LOG_WARN(LOG_APP, "Decode json str, array buffer data is null");
        JsonThrowHandle(env, json_error::NO_YYJSON_ERROR_PARAM_INVALID, "Array buffer data is null");
        return nullptr;
    }
    yyjson_read_flag flg = YYJSON_READ_INSITU;
    yyjson_incr_state *state = yyjson_incr_new(obj->array_buffer_data_, obj->array_buffer_length_, flg, nullptr);

    yyjson_doc *doc = nullptr;
    yyjson_read_err err;
    size_t read_so_far = 0;
    int count = 0;
    do {
        read_so_far += YYJSON_READ_INC_VALUE;
        if (read_so_far > obj->array_buffer_length_) {
            read_so_far = obj->array_buffer_length_;
        }
        doc = yyjson_incr_read(state, read_so_far, &err);
        count++;
        if (err.code != YYJSON_READ_ERROR_MORE) {
            break;
        }
    } while (read_so_far < obj->array_buffer_length_);

    yyjson_incr_free(state);

    OH_LOG_DEBUG(LOG_APP, "ByteLength:%{public}zu, read length:%{public}zu, count:%{public}d",
                 obj->array_buffer_length_, read_so_far, count);

    napi_value result = nullptr;
    if (doc != nullptr) {
        obj->root_ = yyjson_doc_get_root(doc);
        OH_LOG_DEBUG(LOG_APP, "Decode json str, yyjson doc get root end.");
    } else {
        OH_LOG_WARN(LOG_APP, "Decode json str, read error:%{public}s, code:%{public}u at byte position:%{public}lu",
                    err.msg, err.code, err.pos);
        json_error::JsonThrowHandle(env, json_error::NO_YYJSON_ERROR_JSON_STR_INVALID, obj->array_buffer_data_,
                                    obj->array_buffer_length_, err);
    }
    obj->doc_ = doc;

    OH_LOG_DEBUG(LOG_APP, "Decode json str end.");
    return nullptr;
}

napi_value JsonNode::DecodeFromArrayBufferAsync(napi_env env, napi_callback_info info)
{
    napi_value jsThis;
    napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, nullptr);

    JsonNode *obj;
    napi_unwrap_sendable(env, jsThis, reinterpret_cast<void **>(&obj));
    if ((obj->array_buffer_data_ == nullptr) || (obj->array_buffer_length_ == 0)) {
        OH_LOG_WARN(LOG_APP, "Decode json str, array buffer data is null");
        JsonThrowHandle(env, json_error::NO_YYJSON_ERROR_PARAM_INVALID, "Array buffer data is null");
        return nullptr;
    }
    napi_deferred deferred;
    napi_value promise;
    HANDLE_NAPI_ERROR(env, napi_create_promise(env, &deferred, &promise));
    
    napi_value resource_name;
    HANDLE_NAPI_ERROR(env, napi_create_string_utf8(env, "ParseIncAsync", NAPI_AUTO_LENGTH, &resource_name));
    AsyncData* asyncData = new AsyncData{obj, deferred, static_cast<char*>(obj->array_buffer_data_),
        obj->array_buffer_length_, 0, nullptr, false, ""};
    napi_async_work work;
    HANDLE_NAPI_ERROR(env, napi_create_async_work(env, nullptr, resource_name, ParseIncAsync,
                                                            ParseJsonNodeAsyncCompleted, asyncData, &work));
    HANDLE_NAPI_ERROR(env, napi_queue_async_work(env, work));
    return promise;
}

napi_value JsonNode::GetDataObject(napi_env env, napi_callback_info info, JsonDataType dataType)
{
    napi_value jsThis;
    size_t argc = 1;
    napi_value args[1];
    napi_get_cb_info(env, info, &argc, args, &jsThis, nullptr);
    if (argc < 1) {
        OH_LOG_WARN(LOG_APP, "Get data object, para is null.");
        return nullptr;
    }

    size_t path_len = 0;
    napi_status status = napi_get_value_string_utf8(env, args[0], nullptr, 0, &path_len);
    if (status != napi_ok) {
        OH_LOG_WARN(LOG_APP, "Get data object, get pointer data len failed(%{public}d).", status);
        return nullptr;
    }

    OH_LOG_DEBUG(LOG_APP, "Get data object path_len：%{public}zu", path_len);
    char *data = new char[path_len + 1]();
    if (data == nullptr) {
        OH_LOG_WARN(LOG_APP, "Get data object, data is null.");
        JsonThrowHandle(env, json_error::NO_YYJSON_ERROR_MEMORY_FAILED, "Get data object, new failed");
        return nullptr;
    }
    napi_value result = nullptr;
    do {
        status = napi_get_value_string_utf8(env, args[0], data, path_len + 1, &path_len);
        if (status != napi_ok) {
            OH_LOG_WARN(LOG_APP, "Get data object, get pointer data failed(%{public}d).", status);
            break;
        }
        OH_LOG_DEBUG(LOG_APP, "Get data object key：%{public}s", data);

        JsonNode *obj = nullptr;
        status = napi_unwrap_sendable(env, jsThis, (void **)&obj);
        if ((status != napi_ok) || (obj == nullptr) || (obj->root_ == nullptr)) {
            OH_LOG_WARN(LOG_APP, "Get data object, napi unwrap failed(%{public}d)", status);
            break;
        }
        yyjson_val *res = nullptr;
        if (dataType == JSON_DATA_TYPE_POINTER_OBJECT) {
            res = yyjson_ptr_get(obj->root_, data);
            if (res == nullptr) {
                OH_LOG_WARN(LOG_APP, "Get data object, yyjson ptr get failed.");
                break;
            }
        } else if (dataType == JSON_DATA_TYPE_BASIC_OBJECT) {
            res = yyjson_obj_get(obj->root_, data);
            if (!yyjson_is_obj(res)) {
                OH_LOG_WARN(LOG_APP, "Get data object, yyjson is not object.");
                break;
            }
        } else if (dataType == JSON_DATA_TYPE_ARR_OBJECT) {
            res = yyjson_obj_get(obj->root_, data);
            if (!yyjson_is_arr(res)) {
                OH_LOG_WARN(LOG_APP, "Get data object, yyjson is not array.");
                break;
            }
        } else {
            OH_LOG_WARN(LOG_APP, "Get data object, data type(%{public}d) unknown.", dataType);
            break;
        }

        result = CreateNapiValueByJsonValue(env, res);

        OH_LOG_DEBUG(LOG_APP, "Get data object end");
    } while (0);

    delete[] data;
    data = nullptr;

    return result;
}

napi_value JsonNode::At(napi_env env, napi_callback_info info)
{
    OH_LOG_DEBUG(LOG_APP, "Get point object start.");

    napi_value instance = GetDataObject(env, info, JSON_DATA_TYPE_POINTER_OBJECT);

    return instance;
}

napi_value JsonNode::Keys(napi_env env, napi_callback_info info)
{
    OH_LOG_DEBUG(LOG_APP, "Get keys start");
    napi_value jsThis;

    napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, nullptr);

    napi_value arrKeys = nullptr;
    napi_create_array(env, &arrKeys);
    JsonNode *obj = nullptr;
    napi_unwrap_sendable(env, jsThis, (void **)&obj);
    if ((obj == nullptr) || (obj->root_ == nullptr)) {
        OH_LOG_WARN(LOG_APP, "Get keys, napi unwrap sendable fail");
        return arrKeys;
    }

    if (yyjson_is_obj(obj->root_)) {
        yyjson_obj_iter iter = yyjson_obj_iter_with(obj->root_);
        int i = 0;
        yyjson_val *key = yyjson_obj_iter_next(&iter);
        while (key != nullptr) {
            napi_value jsStr;
            napi_create_string_utf8(env, yyjson_get_str(key), NAPI_AUTO_LENGTH, &jsStr);
            napi_set_element(env, arrKeys, i, jsStr);
            i++;
            key = yyjson_obj_iter_next(&iter);
        }
    } else {
        OH_LOG_WARN(LOG_APP, "Get keys, yyjson is not object ");
    }

    return arrKeys;
}

napi_value JsonNode::GetObject(napi_env env, napi_callback_info info)
{
    OH_LOG_DEBUG(LOG_APP, "Get object start.");

    napi_value instance = GetDataObject(env, info, JSON_DATA_TYPE_BASIC_OBJECT);

    return instance;
}

napi_value JsonNode::GetArray(napi_env env, napi_callback_info info)
{
    OH_LOG_DEBUG(LOG_APP, "Get array start.");

    napi_value instance = GetDataObject(env, info, JSON_DATA_TYPE_ARR_OBJECT);

    return instance;
}

napi_value JsonNode::CreateNapiData(napi_env env, yyjson_val *objValue, JsonDataType dataType)
{
    napi_value result = nullptr;
    if (dataType == JSON_DATA_TYPE_STRING) {
        if (yyjson_is_str(objValue)) {
            const char *str = yyjson_get_str(objValue);
            napi_create_string_utf8(env, str, strlen(str), &result);
        } else {
            OH_LOG_INFO(LOG_APP, "Create Napi data, obj is not string.");
        }
    } else if (dataType == JSON_DATA_TYPE_NUMBER) {
        if (yyjson_is_int(objValue)) {
            napi_create_int32(env, yyjson_get_int(objValue), &result);
        } else if (yyjson_is_num(objValue)) {
            napi_create_double(env, yyjson_get_num(objValue), &result);
        } else {
            OH_LOG_INFO(LOG_APP, "Create Napi data, obj is not number(%{public}d).", yyjson_get_type(objValue));
        }
    } else if (dataType == JSON_DATA_TYPE_BIGINT) {
        if (yyjson_is_uint(objValue)) {
            napi_create_bigint_uint64(env, yyjson_get_uint(objValue), &result);
        } else if (yyjson_is_sint(objValue)) {
            napi_create_bigint_int64(env, yyjson_get_sint(objValue), &result);
        } else {
            OH_LOG_INFO(LOG_APP, "Create Napi data, obj is not bigint(%{public}d).", yyjson_get_type(objValue));
        }
    } else if (dataType == JSON_DATA_TYPE_BOOLEAN) {
        if (yyjson_is_bool(objValue)) {
            napi_get_boolean(env, yyjson_get_bool(objValue), &result);
        } else {
            OH_LOG_INFO(LOG_APP, "Create Napi data, obj is not bool(%{public}d).", yyjson_get_type(objValue));
        }
    } else {
        OH_LOG_WARN(LOG_APP, "Create Napi data, data type(%{public}d) unknown.", dataType);
    }
    return result;
}

napi_value JsonNode::GetData(napi_env env, napi_callback_info info, JsonDataType dataType)
{
    napi_value jsThis;
    size_t argc = 1;
    napi_value args[1];

    napi_get_cb_info(env, info, &argc, args, &jsThis, nullptr);
    if (argc < 1) {
        OH_LOG_WARN(LOG_APP, "Get data, para is null.");
        return nullptr;
    }
    JsonNode *obj = nullptr;
    napi_unwrap_sendable(env, jsThis, (void **)&obj);
    if ((obj == nullptr) || (obj->root_ == nullptr)) {
        OH_LOG_INFO(LOG_APP, "Get data, napi unwrap fail");
        return nullptr;
    }
    size_t key_len = 0;
    napi_status status = napi_get_value_string_utf8(env, args[0], nullptr, 0, &key_len);
    if (status != napi_ok) {
        OH_LOG_WARN(LOG_APP, "Get data, get pointer data len failed(%{public}d).", status);
        return nullptr;
    }
    OH_LOG_DEBUG(LOG_APP, "Get data key len：%{public}zu", key_len);
    char *keyData = new char[key_len + 1]();
    if (keyData == nullptr) {
        OH_LOG_WARN(LOG_APP, "Get data, data is null.");
        JsonThrowHandle(env, json_error::NO_YYJSON_ERROR_MEMORY_FAILED, "Get data, malloc failed");
        return nullptr;
    }
    napi_value result = nullptr;
    do {
        status = napi_get_value_string_utf8(env, args[0], keyData, key_len + 1, &key_len);
        if (status != napi_ok) {
            OH_LOG_WARN(LOG_APP, "Get data, get pointer data failed(%{public}d).", status);
            break;
        }
        OH_LOG_DEBUG(LOG_APP, "Get data, key data：%{public}s", keyData);
        yyjson_val *objValue = yyjson_obj_get(obj->root_, keyData);
        if (objValue == nullptr) {
            OH_LOG_WARN(LOG_APP, "Get data, get obj data failed.");
            break;
        }    
        result = CreateNapiData(env, objValue, dataType);
    } while (0);
    delete[] keyData;
    keyData = nullptr;

    return result;
}

napi_value JsonNode::GetString(napi_env env, napi_callback_info info)
{
    OH_LOG_DEBUG(LOG_APP, "Get string start");

    napi_value result = GetData(env, info, JSON_DATA_TYPE_STRING);

    return result;
}

napi_value JsonNode::GetNumber(napi_env env, napi_callback_info info)
{
    OH_LOG_DEBUG(LOG_APP, "Get number start");

    napi_value result = GetData(env, info, JSON_DATA_TYPE_NUMBER);

    return result;
}

napi_value JsonNode::GetBigInt(napi_env env, napi_callback_info info)
{
    OH_LOG_DEBUG(LOG_APP, "Get bigint start");

    napi_value result = GetData(env, info, JSON_DATA_TYPE_BIGINT);

    return result;
}

napi_value JsonNode::GetBoolean(napi_env env, napi_callback_info info)
{
    OH_LOG_DEBUG(LOG_APP, "Get bool start");

    napi_value result = GetData(env, info, JSON_DATA_TYPE_BOOLEAN);

    return result;
}

static napi_status CreateNapiValueFromUInt(napi_env env, uint64_t uintVal, napi_value* result)
{
    if (uintVal <= yyjson_util::numberMaxValue) {
        return napi_create_int64(env, static_cast<int64_t>(uintVal), result);
    }
    return napi_create_bigint_uint64(env, uintVal, result);
}

static napi_status CreateNapiValueFromSInt(napi_env env, int64_t sintVal, napi_value* result)
{
    if (sintVal >= yyjson_util::numberMinValue && sintVal <= yyjson_util::numberMaxValue) {
        return napi_create_int64(env, sintVal, result);
    }
    return napi_create_bigint_int64(env, sintVal, result);
}

napi_value JsonNode::CreateNapiValueByJsonValue(napi_env env, yyjson_val *objValue)
{
    if (objValue == nullptr) {
        return nullptr;
    }
    napi_value result = nullptr;
    if (yyjson_is_str(objValue)) {
        napi_create_string_utf8(env, unsafe_yyjson_get_str(objValue), NAPI_AUTO_LENGTH, &result);
    } else if (yyjson_is_uint(objValue)) {
        uint64_t uintVal = unsafe_yyjson_get_uint(objValue);
        CreateNapiValueFromUInt(env, uintVal, &result);
    } else if (yyjson_is_sint(objValue)) {
        int64_t sintVal = unsafe_yyjson_get_sint(objValue);
        CreateNapiValueFromSInt(env, sintVal, &result);
    } else if (yyjson_is_num(objValue)) {
        napi_create_double(env, yyjson_get_num(objValue), &result);
    } else if (yyjson_is_bool(objValue)) {
        napi_get_boolean(env, yyjson_get_bool(objValue), &result);
    } else if (yyjson_is_arr(objValue) || yyjson_is_obj(objValue)) {
        napi_value cons;
        napi_status status = napi_get_reference_value(env, g_ref, &cons);
        if (status != napi_ok) {
            OH_LOG_WARN(LOG_APP, "Get napi value, napi get reference value failed(%{public}d).", status);
            return nullptr;
        }
        status = napi_new_instance(env, cons, 0, nullptr, &result);
        if (status != napi_ok) {
            OH_LOG_WARN(LOG_APP, "Get napi value, napi new instance failed(%{public}d).", status);
            return nullptr;
        }
        JsonNode *newObj = nullptr;
        status = napi_unwrap_sendable(env, result, (void **)&newObj);
        if (status != napi_ok) {
            OH_LOG_WARN(LOG_APP, "Get napi value, newObj napi unwrap failed(%{public}d).", status);
            return nullptr;
        }
        newObj->root_ = objValue;
        OH_LOG_DEBUG(LOG_APP, "Get napi value end");
    } else if (yyjson_is_null(objValue)) {
        napi_get_null(env, &result);
    } else {
        OH_LOG_WARN(LOG_APP, "Get napi value, obj is not bigint(%{public}d).", yyjson_get_type(objValue));
    }
    return result;
}

napi_value JsonNode::GetArrayValue(napi_env env, napi_callback_info info)
{
    OH_LOG_DEBUG(LOG_APP, "Get array value start");

    napi_value jsThis;
    size_t argc = 1;
    napi_value args[1];

    napi_get_cb_info(env, info, &argc, args, &jsThis, nullptr);
    if (argc < 1) {
        OH_LOG_WARN(LOG_APP, "Get array value, para is null.");
        return nullptr;
    }
    JsonNode *obj = nullptr;
    napi_unwrap_sendable(env, jsThis, (void **)&obj);
    if ((obj == nullptr) || (obj->root_ == nullptr)) {
        OH_LOG_WARN(LOG_APP, "Get array value, napi unwrap fail");
        return nullptr;
    }

    uint32_t index = 0;
    if (napi_get_value_uint32(env, args[0], &index) != napi_ok) {
        OH_LOG_WARN(LOG_APP, "Get array value, para is not uint32.");
        return nullptr;
    }
    OH_LOG_DEBUG(LOG_APP, "Get array value index：%{public}u", index);

    size_t arrSize = yyjson_arr_size(obj->root_);
    if (index >= arrSize) {
        OH_LOG_WARN(LOG_APP, "Get array value, index >= arrSize(%{public}u)", arrSize);
        return nullptr;
    }

    yyjson_val *objValue = yyjson_arr_get(obj->root_, index);
    napi_value result = CreateNapiValueByJsonValue(env, objValue);
    if (result == nullptr) {
        OH_LOG_WARN(LOG_APP, "Get array value failed");
    }

    return result;
}

napi_value JsonNode::GetArrayValues(napi_env env, napi_callback_info info)
{
    OH_LOG_DEBUG(LOG_APP, "Get array values start");

    napi_value jsThis;
    napi_get_cb_info(env, info, 0, nullptr, &jsThis, nullptr);

    napi_value arrKeys = nullptr;
    napi_create_array(env, &arrKeys);

    JsonNode *obj = nullptr;
    napi_unwrap_sendable(env, jsThis, (void **)&obj);
    if ((obj == nullptr) || (obj->root_ == nullptr)) {
        OH_LOG_INFO(LOG_APP, "Get array values, napi unwrap fail");
        return arrKeys;
    }

    size_t arrSize = yyjson_arr_size(obj->root_);
    for (size_t index = 0; index < arrSize; index++) {
        yyjson_val *objValue = yyjson_arr_get(obj->root_, index);
        napi_value napiValue = CreateNapiValueByJsonValue(env, objValue);
        if (napiValue == nullptr) {
            OH_LOG_INFO(LOG_APP, "Get values value failed");
        }
        napi_set_element(env, arrKeys, index, napiValue);
    }

    return arrKeys;
}

napi_value JsonNode::ElementCount(napi_env env, napi_callback_info info)
{
    OH_LOG_DEBUG(LOG_APP, "Get node element count start");

    napi_value jsThis;
    napi_get_cb_info(env, info, 0, nullptr, &jsThis, nullptr);

    napi_value result = nullptr;
    size_t length = 0;

    JsonNode *obj = nullptr;
    napi_unwrap_sendable(env, jsThis, (void **)&obj);
    if ((obj == nullptr) || (obj->root_ == nullptr)) {
        OH_LOG_WARN(LOG_APP, "Get node element count, napi unwrap fail");
        return nullptr;
    }

    if (yyjson_is_arr(obj->root_)) {
        length = yyjson_arr_size(obj->root_);
    } else if (yyjson_is_obj(obj->root_)) {
        length = yyjson_obj_size(obj->root_);
    } else {
        OH_LOG_WARN(LOG_APP, "Get node element count, it is not array or object");
    }
    napi_create_uint32(env, length, &result);

    return result;
}

JsonNodeType JsonNode::GetValType(yyjson_val *val)
{
    JsonNodeType type = JSON_NODE_TYPE_INVALID;
    if (yyjson_is_arr(val)) {
        type = JSON_NODE_TYPE_ARRAY;
    } else if (yyjson_is_obj(val)) {
        type = JSON_NODE_TYPE_OBJECT;
    } else if (yyjson_is_str(val)) {
        type = JSON_NODE_TYPE_STRING;
    } else if (yyjson_is_uint(val)) {
        type = JSON_NODE_TYPE_UBIGINT;
    } else if (yyjson_is_sint(val)) {
        type = JSON_NODE_TYPE_BIGINT;
    } else if (yyjson_is_int(val)) {
        type = JSON_NODE_TYPE_INT;
    } else if (yyjson_is_num(val)) {
        type = JSON_NODE_TYPE_DOUBLE;
    } else if (yyjson_is_bool(val)) {
        type = JSON_NODE_TYPE_BOOLEAN;
    } else if (yyjson_is_null(val)) {
        type = JSON_NODE_TYPE_NULL;
    } else {
        yyjson_type type = yyjson_get_type(val);
        OH_LOG_WARN(LOG_APP, "Get value type:%{public}d", type);
    }

    return type;
}

napi_value JsonNode::GetType(napi_env env, napi_callback_info info)
{
    OH_LOG_DEBUG(LOG_APP, "Get node type start");

    napi_value jsThis;
    napi_get_cb_info(env, info, 0, nullptr, &jsThis, nullptr);

    JsonNode *obj = nullptr;
    napi_unwrap_sendable(env, jsThis, (void **)&obj);
    if ((obj == nullptr) || (obj->root_ == nullptr)) {
        OH_LOG_WARN(LOG_APP, "Get node type, napi unwrap fail");
        return nullptr;
    }

    napi_value result = nullptr;
    JsonNodeType type = GetValType(obj->root_);
    napi_create_int32(env, type, &result);

    return result;
}

yyjson_val *JsonNode::GetValueByKey(napi_env env, napi_callback_info info)
{
    OH_LOG_DEBUG(LOG_APP, "Get value by key start");

    napi_value jsThis;
    size_t argc = 1;
    napi_value args[1];

    napi_get_cb_info(env, info, &argc, args, &jsThis, nullptr);
    if (argc < 1) {
        OH_LOG_WARN(LOG_APP, "Get value by key, para is null.");
        return nullptr;
    }
    JsonNode *obj = nullptr;
    napi_unwrap_sendable(env, jsThis, (void **)&obj);
    if ((obj == nullptr) || (obj->root_ == nullptr)) {
        OH_LOG_WARN(LOG_APP, "Get value by key, napi unwrap fail");
        return nullptr;
    }
    size_t key_len = 0;
    napi_status status = napi_get_value_string_utf8(env, args[0], nullptr, 0, &key_len);
    if (status != napi_ok) {
        OH_LOG_WARN(LOG_APP, "Get value by key, get pointer data len failed(%{public}d).", status);
        return nullptr;
    }

    OH_LOG_DEBUG(LOG_APP, "Get key value type key_len：%{public}zu", key_len);
    char *keyData = new char[key_len + 1]();
    if (keyData == nullptr) {
        OH_LOG_WARN(LOG_APP, "Get value by key, data is null.");
        JsonThrowHandle(env, json_error::NO_YYJSON_ERROR_MEMORY_FAILED, "Get value by key, malloc failed");
        return nullptr;
    }

    yyjson_val *objValue = nullptr;
    do {
        status = napi_get_value_string_utf8(env, args[0], keyData, key_len + 1, &key_len);
        if (status != napi_ok) {
            OH_LOG_WARN(LOG_APP, "Get value by key, get pointer data failed(%{public}d).", status);
            break;
        }
        OH_LOG_DEBUG(LOG_APP, "Get value by key, key data：%{public}s", keyData);

        objValue = yyjson_obj_get(obj->root_, keyData);
        if (objValue == nullptr) {
            OH_LOG_INFO(LOG_APP, "Get value by key, yyjson obj get failed(%{public}d).", status);
            break;
        }
    } while (0);

    delete[] keyData;
    keyData = nullptr;

    return objValue;
}

napi_value JsonNode::GetValueTypeByKey(napi_env env, napi_callback_info info)
{
    OH_LOG_DEBUG(LOG_APP, "Get value type by key to start");

    napi_value result = nullptr;
    yyjson_val *objValue = GetValueByKey(env, info);
    if (objValue != nullptr) {
        JsonNodeType type = GetValType(objValue);
        napi_create_int32(env, type, &result);
    }
    return result;
}

napi_value JsonNode::GetValueTypeByIndex(napi_env env, napi_callback_info info)
{
    OH_LOG_DEBUG(LOG_APP, "Get array value type by index to start");

    napi_value jsThis;
    size_t argc = 1;
    napi_value args[1];

    napi_get_cb_info(env, info, &argc, args, &jsThis, nullptr);
    if (argc < 1) {
        OH_LOG_WARN(LOG_APP, "Get array value type by index, para is null.");
        return nullptr;
    }
    JsonNode *obj = nullptr;
    napi_unwrap_sendable(env, jsThis, (void **)&obj);
    if ((obj == nullptr) || (obj->root_ == nullptr)) {
        OH_LOG_WARN(LOG_APP, "Get array value type by index, napi unwrap fail");
        return nullptr;
    }

    uint32_t index = 0;
    if (napi_get_value_uint32(env, args[0], &index) != napi_ok) {
        OH_LOG_WARN(LOG_APP, "Get array value type by index, para is not uint32.");
        return nullptr;
    }
    OH_LOG_DEBUG(LOG_APP, "Get array value type index：%{public}u", index);

    size_t arrSize = yyjson_arr_size(obj->root_);
    if (index >= arrSize) {
        OH_LOG_WARN(LOG_APP, "Get array value by index, index >= arrSize(%{public}u)", arrSize);
        return nullptr;
    }
    napi_value result = nullptr;
    yyjson_val *objValue = yyjson_arr_get(obj->root_, index);
    if (objValue != nullptr) {
        JsonNodeType type = GetValType(objValue);
        napi_create_int32(env, type, &result);
    }

    return result;
}

napi_value JsonNode::HasKey(napi_env env, napi_callback_info info)
{
    OH_LOG_DEBUG(LOG_APP, "Has key start");

    napi_value result = nullptr;
    bool isExist = false;
    yyjson_val *objValue = GetValueByKey(env, info);
    if (objValue != nullptr) {
        isExist = true;
    }
    napi_get_boolean(env, isExist, &result);

    return result;
}

napi_value JsonNode::ToString(napi_env env, napi_callback_info info)
{
    OH_LOG_DEBUG(LOG_APP, "To string start");

    napi_value jsThis;
    napi_get_cb_info(env, info, 0, nullptr, &jsThis, nullptr);
    JsonNode *obj = nullptr;
    napi_unwrap_sendable(env, jsThis, (void **)&obj);
    if ((obj == nullptr) || (obj->root_ == nullptr)) {
        OH_LOG_WARN(LOG_APP, "To string, napi unwrap fail");
        return nullptr;
    }

    napi_value result = nullptr;
    size_t len = 0;
    char *valueStr = yyjson_val_write(obj->root_, 0, &len);
    if (valueStr != nullptr) {
        napi_create_string_utf8(env, valueStr, len, &result);
        free(valueStr);
    }

    return result;
}

napi_value JsonNode::ToBuffer(napi_env env, napi_callback_info info)
{
    OH_LOG_DEBUG(LOG_APP, "To buffer start");

    napi_value jsThis;
    napi_get_cb_info(env, info, 0, nullptr, &jsThis, nullptr);
    JsonNode *obj = nullptr;
    napi_unwrap_sendable(env, jsThis, (void **)&obj);
    if ((obj == nullptr) || (obj->root_ == nullptr)) {
        OH_LOG_WARN(LOG_APP, "To buffer, napi unwrap fail");
        return nullptr;
    }

    napi_value result = nullptr;
    size_t len = 0;
    char *valueStr = yyjson_val_write(obj->root_, 0, &len);
    if (valueStr != nullptr) {
        void *buf = nullptr;
        napi_create_arraybuffer(env, len, &buf, &result); // buf no free
        if (memcpy_s(buf, len, valueStr, len) != EOK) {
            OH_LOG_WARN(LOG_APP, "To buffer, data copy fail");
            free(valueStr);
            return nullptr;
        }
        free(valueStr);
    }

    return result;
}

napi_value JsonNode::ToESObject(napi_env env, napi_callback_info info)
{
    OH_LOG_DEBUG(LOG_APP, "To ESObject start");

    napi_value jsThis;
    napi_get_cb_info(env, info, 0, nullptr, &jsThis, nullptr);
    JsonNode *obj = nullptr;
    napi_unwrap_sendable(env, jsThis, (void **)&obj);
    if ((obj == nullptr) || (obj->root_ == nullptr)) {
        OH_LOG_WARN(LOG_APP, "To ESObject, napi unwrap fail");
        return nullptr;
    }

    return yyjson_util::YYJSONToNapi(env, obj->root_, JSONNODE);
}

napi_value JsonNode::GetValue(napi_env env, napi_callback_info info)
{
    OH_LOG_DEBUG(LOG_APP, "Get value start");

    size_t argc = 1;
    napi_value args[1];
    napi_value jsThis;
    napi_value result = nullptr;

    napi_get_cb_info(env, info, &argc, args, &jsThis, nullptr);
    if (argc < 1) {
        OH_LOG_WARN(LOG_APP, "Get value, para is null.");
        return nullptr;
    }
    napi_valuetype type = napi_undefined;
    napi_typeof(env, args[0], &type);
    OH_LOG_DEBUG(LOG_APP, "Get value type:%{public}d", type);
    if (type != napi_null) {
        yyjson_val *objValue = GetValueByKey(env, info);
        if (objValue != nullptr) {
            result = CreateNapiValueByJsonValue(env, objValue);
        }
    } else {
        JsonNode *obj = nullptr;
        napi_unwrap_sendable(env, jsThis, (void **)&obj);
        if ((obj == nullptr) || (obj->root_ == nullptr)) {
            OH_LOG_WARN(LOG_APP, "Get value, napi unwrap fail");
            return nullptr;
        }
        result = CreateNapiValueByJsonValue(env, obj->root_);
    }

    return result;
}

napi_value JsonNode::GetValues(napi_env env, napi_callback_info info)
{
    OH_LOG_DEBUG(LOG_APP, "Get values start");

    napi_value jsThis = nullptr;

    napi_value arrKeys = nullptr;
    napi_create_array(env, &arrKeys);

    napi_get_cb_info(env, info, 0, nullptr, &jsThis, nullptr);
    JsonNode *obj = nullptr;
    napi_unwrap_sendable(env, jsThis, (void **)&obj);
    if ((obj == nullptr) || (obj->root_ == nullptr)) {
        OH_LOG_WARN(LOG_APP, "Get values, napi unwrap fail");
        return arrKeys;
    }

    if (yyjson_is_obj(obj->root_)) {
        yyjson_obj_iter iter = yyjson_obj_iter_with(obj->root_);
        int i = 0;
        yyjson_val *key = yyjson_obj_iter_next(&iter);
        while (key != nullptr) {
            yyjson_val *val = yyjson_obj_iter_get_val(key);
            napi_value napiValue = CreateNapiValueByJsonValue(env, val);
            napi_set_element(env, arrKeys, i, napiValue);
            i++;
            key = yyjson_obj_iter_next(&iter);
        }
    } else {
        JsonNodeType type = GetValType(obj->root_);
        OH_LOG_DEBUG(LOG_APP, "Get values, yyjson is not object,type:%{public}d ", type);
        napi_value napiValue = CreateNapiValueByJsonValue(env, obj->root_);
        napi_set_element(env, arrKeys, 0, napiValue);
    }

    return arrKeys;
}

napi_value JsonNode::Init(napi_env env, napi_value exports)
{
    napi_property_descriptor properties[] = {
        {"decodeFromArrayBuffer", nullptr, DecodeFromArrayBuffer, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"decodeFromArrayBufferAsync", nullptr, DecodeFromArrayBufferAsync, nullptr, nullptr, nullptr, napi_default,
            nullptr},
        {"at", nullptr, At, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"keys", nullptr, Keys, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getObject", nullptr, GetObject, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getArray", nullptr, GetArray, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getArrayValue", nullptr, GetArrayValue, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getArrayValues", nullptr, GetArrayValues, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getString", nullptr, GetString, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getNumber", nullptr, GetNumber, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getBigInt", nullptr, GetBigInt, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getBoolean", nullptr, GetBoolean, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"elementCount", nullptr, ElementCount, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getType", nullptr, GetType, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getValueTypeByKey", nullptr, GetValueTypeByKey, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getValueTypeByIndex", nullptr, GetValueTypeByIndex, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"hasKey", nullptr, HasKey, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"toString", nullptr, ToString, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"toBuffer", nullptr, ToBuffer, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"toESObject", nullptr, ToESObject, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getValue", nullptr, GetValue, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"values", nullptr, GetValues, nullptr, nullptr, nullptr, napi_default, nullptr}};

    napi_value cons;
    napi_define_sendable_class(env, "RawJsonNode", NAPI_AUTO_LENGTH, New, nullptr,
                               sizeof(properties) / sizeof(properties[0]), properties, nullptr, &cons);
    napi_create_reference(env, cons, 1, &g_ref);
    napi_set_named_property(env, exports, "RawJsonNode", cons);

    return exports;
}